Crafty.init(320, 480);
Crafty.background('#eee');
Crafty.timer.FPS(24);

const GROUNDX = 18;
const GROUNDY = 18;

var Printer = (width, height, cell, offset, sprite) => {
    var points = _.map(_.range(0, width * height), (v) => {
        return Crafty.e('2D, DOM, ' + sprite).attr({
            x: offset.x + (v % width) * cell,
            y: offset.y + Math.floor(v / width) * cell,
            w: cell,
            h: cell,
        });
    });

    return (data) => {
        _.each(points, (v, i) => {
            var x = parseInt(data[i]);
            v.sprite(x, 0);
        });
    };
};

Crafty.c('Printer', {
    init: function() {
        this.requires('Sprite');
    }
});

Crafty.load({
    sprites: {
        '4PixelNumber.png': {
            tile: 4,
            tileh: 4,
            map: {
                SpriteNumber: [0, 0],
            },
        },
        'key.png': {
            tile: 8,
            tileh: 8,
            map: {
                SpriteKey: [0, 0],
            },
        },
        'block.png': {
            tile: 8,
            tileh: 8,
            map: {
                SpriteBlock: [0, 0],
            }
        },
    },
}, () => {
    p = Printer(4, 1, 16, {x: 8, y:8}, 'SpriteNumber')
    b = Printer(GROUNDX, GROUNDY, 16, {x: 8, y: 32}, 'SpriteBlock');
    k = Printer(6, 3, 48, {x: 8, y: 320}, 'SpriteKey');
    k('010000101001010000');
    Crafty.pixelart(true);
    run();
});

var init = (w, h) => {
    return _.map(_.range(w * h), (v) => {
        return 0;
        return v%w == 0 || v%w == w-1 || Math.floor(v/w) == 0 || Math.floor(v/w) == h-1 ? '1' : '0';
    });
};

var spawn = () => {
    return [Crafty.math.randomInt(0, GROUNDX - 1), Crafty.math.randomInt(0, GROUNDY - 1)];    
};

var snake = [[6,4], [5,4], [4,4]];
var egg = spawn();
const UP = [0, -1];
const DOWN = [0, 1];
const LEFT = [-1, 0];
const RIGHT = [1, 0];

const KEY_PAUSE = 32;
const GAMEPAD_PAUSE = 0;

const keymap = {
    37: LEFT,
    65: LEFT,
    38: UP,
    87: UP,
    39: RIGHT,
    68: RIGHT,
    40: DOWN,
    83: DOWN,
};

const gamepadMap = {
    14: LEFT,
    12: UP,
    15: RIGHT,
    13: DOWN,
};

var dir = RIGHT;

var run = () => {
    var virtualEntity = Crafty.e('Keyboard').bind('KeyDown', function (e) {
        if (e.key == KEY_PAUSE) {
            Crafty.pause();
            return;
        }
        if (keymap[e.key]) {
            // 只允许转弯，不允许相同方向或相反方向
            if ((dir[0] + keymap[e.key][0]) % 2 == 0) {
                return;
            }
            dir = keymap[e.key];
        }
    }).bind('EnterFrame', function (e) {
        // 如果使用手柄
        gpk = getGamepadKeys();
        if (_.contains(gpk, GAMEPAD_PAUSE)) {
            Crafty.pause();
            return;
        }
        var gpdir = _.find(gpk, (v) => typeof gamepadMap[v] !== 'undefined');
        if (gpdir) {
            if ((dir[0] + gamepadMap[gpdir][0]) % 2 != 0) {
                dir = gamepadMap[gpdir];
            }
        }

        // 非关键帧到此为止
        if (Crafty.frame() % 7) {
            return;
        }

        var head = _.clone(_.first(snake));
        head[0] += dir[0];
        head[1] += dir[1];
        if (_.contains(_.initial(snake), head) || head[0] < 0 || head[0] > GROUNDX - 1 || head[1] < 0 || head[1] > GROUNDY - 1) {
            console.log('GAME OVER');
        }

        var isEat = _.isEqual(head, egg);
        snake = _.union([head], isEat ? snake : _.initial(snake));
        if (isEat) {
            egg = spawn();
        }

        var data = init(GROUNDX, GROUNDX);
        _.each(snake, (v) => {
            var idx = v[1] * GROUNDX + v[0];
            data[idx] = '1';
        });
        data[egg[1] * GROUNDX + egg[0]] = '2';
        b(data);

        var score = snake.length - 3;
        var printScore = '0000'.substr(0, 4 - score.toString().length) + score;
        p(printScore);
    });
};

var getGamepadKeys = () => {
    if (!navigator.getGamepads || !navigator.getGamepads() || !navigator.getGamepads()[0]) {
        return;
    }
    return _.select(_.map(navigator.getGamepads()[0].buttons, (v, i) => v.pressed ? i : -1), (v) => v >= 0);
};
